package com.cheng.admin.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cheng.admin.constant.Constants;
import com.cheng.admin.dao.OrderMapper;
import com.cheng.admin.model.Order;
import com.cheng.admin.rabbitmq.MqConstants;
import com.cheng.admin.service.CouponService;
import com.cheng.admin.service.DeliveryService;
import com.cheng.admin.service.OrderService;
import com.cheng.admin.service.order.process.IOrderPreprocess;
import com.cheng.admin.service.order.states.OrderState;
import com.cheng.common.config.WeChatKeys;
import com.cheng.common.domain.BaseSearchVO;
import com.cheng.common.exception.CustomException;
import com.cheng.system.service.UserService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ijpay.core.kit.HttpKit;
import com.ijpay.core.kit.WxPayKit;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 订单 服务实现类
 * 一共有三种订单
 * 1. 普通寄信订单
 * 2. 明信片订单
 * 3. 商品订单
 *
 * @author 起凡
 * @since 2022-01-17
 */
@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
    @Autowired
    CouponService couponService;
    @Autowired
    DeliveryService deliveryService;
    @Autowired
    Map<String, IOrderPreprocess> orderPreprocessMap;

    @Autowired
    List<OrderState> orderStates;
    @Autowired
    UserService userService;
    @Autowired
    WeChatKeys weChatKeys;
    @Autowired
    AmqpTemplate amqpTemplate;

    /**
     * 提交订单 <br/>
     * 1. 获取订单类型，然后得到该类型的 <strong>preprocessService</strong> <br/>
     * 2. 订单价格计算（看具体是什么订单）-> 快递费用计算 -> 优惠计算（目前没开放）
     *
     * @param order 订单表单
     * @return
     */
    @Override
    @Transactional
    public Order create(Order order) {
        order.setUserId(StpUtil.getLoginIdAsLong()).setOriginPrice(BigDecimal.ZERO)
                .setFinalPrice(BigDecimal.ZERO);
        // 获取id，先将订单保存到数据库，得到自动生成的订单id。
        // 有@Transactional 下面出错会自动回滚。
        save(order);
        IOrderPreprocess preprocessService = orderPreprocessMap.get(order.getType());
        if (preprocessService == null) {
            throw new CustomException("订单类型错误");
        }
        // 根据订单类型计算价格
        preprocessService.preprocess(order);
        // 快递费用计算
        deliveryService.preprocess(order);
        // 这个目前没用到
        couponService.preprocess(order);
        // 得到最终价格 原始价格-优惠券价格=最终价格
        order.setFinalPrice(order.getOriginPrice().subtract(order.getCouponPrice()));
        // 更新刚刚保存的订单
        updateById(order);
        // 一段时间内未支付的订单会自动被取消
        amqpTemplate.convertAndSend(MqConstants.DELAY_EXCHANGE_ORDER, MqConstants.DELAY_QUEUE_ORDER_CANCEL, order.getId(),
                message -> {
                    message.getMessageProperties().setDelay(MqConstants.DELAYED_ORDER_CANCEL_TIME);
                    return message;
                });
        return order;
    }

    @Override
    public Order get(Long id) {
        return getBaseMapper().getOrder(id);
    }

    @Override
    public PageInfo<Order> listOrder(BaseSearchVO baseSearchVO) {
        PageHelper.startPage(baseSearchVO.getPageNum(), baseSearchVO.getPageSize());
        PageInfo<Order> pageInfo = new PageInfo<>(getBaseMapper().listOrder(baseSearchVO));
        return pageInfo;
    }

    @Override
    public String callBack(HttpServletRequest request) throws Exception {
        Map<String, String> map = new HashMap<>(12);
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String serialNo = request.getHeader("Wechatpay-Serial");
        String signature = request.getHeader("Wechatpay-Signature");
        log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
        String result = HttpKit.readData(request);
        log.info("支付通知密文 {}", result);

        // 需要通过证书序列号查找对应的证书，verifyNotify 中有验证证书的序列号
        String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
                weChatKeys.getApiKey3(), weChatKeys.getPlatformCertPath());

        log.info("支付通知明文 {}", plainText);

        if (StrUtil.isNotEmpty(plainText)) {
            JSONObject jsonObject = JSON.parseObject(plainText);
            map.put("code", "SUCCESS");
            map.put("message", "SUCCESS");
            String orderNo = (String) jsonObject.get("out_trade_no");
            Order order = getOne(Wrappers.<Order>lambdaQuery().eq(Order::getOrderNo, orderNo));
            String wxOrder = (String) jsonObject.get("transaction_id");
            //目前订单不需要后续回调处理
//                orderCallbackProcessMap.get(order.getType()).callbackProcess(order);
            order.setPayOrderNo(wxOrder);
            order.setOrderStatus(Constants.ORDER_STATUS_UNSHIPPED);
            order.setPaymentTime(LocalDateTime.now());
            updateById(order);
            log.info("支付成功");
            return JSONUtil.toJsonStr(map);
        } else {
            throw new CustomException("签名错误");
        }
    }

    /**
     * 根据订单目前的状态去执行pay操作
     * 下面的方法道理一样
     *
     * @param id 订单id
     * @return
     * @throws Exception
     */
    public String pay(Long id) throws Exception {
        Order order = getById(id);
        return orderStates.get(order.getOrderStatus()).pay(id);
    }

    public Map<String, String> ship(Long id, String logisticsNo) {
        Order order = getById(id);
        return orderStates.get(order.getOrderStatus()).ship(id, logisticsNo);
    }

    public Map<String, String> confirm(Long id) {
        Order order = getById(id);
        return orderStates.get(order.getOrderStatus()).confirm(id);
    }

    public Map<String, String> cancel(Long id) {
        Order order = getById(id);
        return orderStates.get(order.getOrderStatus()).cancel(id);
    }
}
